package com.github.ecbp.common.security.config;

import com.alibaba.fastjson.JSON;
import com.github.ecbp.common.constant.CacheKeyConstant;
import com.github.ecbp.common.redis.RedisUtil;
import com.github.ecbp.common.security.vo.UrlResource;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

/**
 * 扫描器, 用来扫描项目中所有的requestMapping地址信息, 对于没有设置资源的地址信息保存到缓冲中,等待定时器处理
 */
@Component
public class ControllerMethodScanner {
    public static final String UN_HANDLED_URL = "UN_HANDLED_URL";
    public static final String HANDLED_URL = "HANDLED_URL";
    public static final String URL_DETAIL = "URL_DETAIL";

    @Value("${spring.application.name}")
    private String projectName;

    @Autowired
    private JwtTokenConfig jwtTokenConfig;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private WebApplicationContext applicationContext;

    @PostConstruct
    public void scanAllControllerMethod() {
        // 获取url与类和方法的对应信息
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        Set<Map.Entry<RequestMappingInfo, HandlerMethod>> entries = map.entrySet();

        PathMatcher pathMatcher = new AntPathMatcher();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : entries) {
            // 获取url的Set集合，一个方法可能对应多个url
            Set<String> patterns = entry.getKey().getPatternsCondition().getPatterns();
            HandlerMethod handlerMethod = entry.getValue();

            for (String url : patterns) {
                if (jwtTokenConfig.getUrls().stream().distinct().noneMatch(path -> pathMatcher.match(path, url))
                        && jwtTokenConfig.getLoginUrls().stream().distinct().noneMatch(path -> pathMatcher.match(path, url))) {
                    String value = projectName + CacheKeyConstant.PATH_SEPARATOR + url;
                    Boolean sismember = redisUtil.sismember(HANDLED_URL, value);
                    if (null != sismember && !sismember) {
                        redisUtil.sadd(UN_HANDLED_URL, value);

                        Method method = handlerMethod.getMethod();
                        if (method.isAnnotationPresent(ApiOperation.class)) {
                            ApiOperation annotation = method.getAnnotation(ApiOperation.class);
                            UrlResource resource = new UrlResource();
                            resource.setProjectName(projectName);
                            resource.setName(annotation.value());
                            resource.setUrl(url);
                            resource.setDescription(annotation.notes());
                            redisUtil.hset(URL_DETAIL, value, JSON.toJSONString(resource));
                        }
                    }
                }
            }
        }
    }
}
